#ifndef SHIFTIN_H
#define SHIFTIN_H

#include <Arduino.h>
#include "KeyBoard_conf.h"
#include "LVGLDisplayToLCD.h"
#include <stdint.h>
#include "Transmit.h"

template <byte chipCount, typename ShiftType>
class _ShiftIn
{
private:
	byte ploadPin;
	byte clockEnablePin;
	byte dataPin;
	byte clockPin;

	const uint16_t dataWidth;
	uint8_t pulseWidth;

	uint8_t lastState;
	uint8_t currentState;

public:
	_ShiftIn() : dataWidth(chipCount * 8), pulseWidth(5), lastState(0), currentState(0) {}

	// setup all pins
	void begin(int pload, int clockEN, int data, int clock)
	{
		pinMode(ploadPin = pload, OUTPUT);
		pinMode(clockEnablePin = clockEN, OUTPUT);
		pinMode(dataPin = data, INPUT);
		pinMode(clockPin = clock, OUTPUT);
	}

	inline uint8_t getPulseWidth() { return pulseWidth; }
	inline void setPulseWidth(uint8_t value) { pulseWidth = value; }
	inline uint16_t getDataWidth() { return dataWidth; }
	// whether some value has changed
	inline uint8_t hasChanged() { return lastState != currentState; }
	// whether the value with index 'id' has changed
	inline uint8_t hasChanged(int id) { return state(id) != last(id); }
	// returns the state from the last frame
	inline uint8_t getLast() { return lastState; }
	// returns the current state
	inline uint8_t getCurrent() { return currentState; }
	// whether button 'id' is pressed or not
	inline uint8_t state(int id) { return bitRead(currentState, id); }
	// whether button 'id' was pressed in the last frame
	inline uint8_t last(int id) { return bitRead(lastState, id); }
	// whether button 'id' is now pressed, but wasn't pressed in the last frame
	inline uint8_t pressed(int id) { return !last(id) && state(id); }
	// whether button 'id' is now released, but was pressed in the last frame
	inline uint8_t released(int id) { return last(id) && !state(id); }

	// read in data from shift register and return the new value
	uint8_t read()
	{
		lastState = currentState;
		uint8_t result = 0;

		digitalWrite(clockEnablePin, HIGH);
		digitalWrite(ploadPin, LOW);
		delayMicroseconds(pulseWidth);
		digitalWrite(ploadPin, HIGH);
		digitalWrite(clockEnablePin, LOW);

		for (uint16_t i = 0; i < dataWidth; i++)
		{
			uint8_t value = digitalRead(dataPin);
			result |= (value << ((dataWidth - 1) - i));
			digitalWrite(clockPin, HIGH);
			delayMicroseconds(pulseWidth);
			digitalWrite(clockPin, LOW);
		}
		currentState = result;
		return result;
	}

	// transmit and display the changed key
	void operateChangedKey()
	{
		for (uint8_t i = 0; i < this->getDataWidth(); i++)
			if (this->hasChanged(i))
			{
				lvglDisplay(NumToKey[i]);
				Serial.println(NumToKey[i]);
				js_eval("keyboard_press");
				vTaskDelay(50);
			}

		lv_timer_handler();
		lv_tick_inc(5);
		vTaskDelay(5);
	}

	// same as read, but it returns whether something has changed or not
	uint8_t update()
	{
		return read() != lastState;
	}
};
template <byte chipCount>
class ShiftIn : public _ShiftIn<chipCount, uint64_t>
{
};

template <>
class ShiftIn<1> : public _ShiftIn<1, uint8_t>
{
};


#endif